終於要開始建立 ECS service 了!今天我們的目標是讓 Laravel 成功在 ECS service 上運作~
ECS service 是 AWS ECS 用來執行長期運作且 stateless 的 application 的功能,我們用 Laravel 開發的 web application 就是需要長期運作的 application,不像影片轉檔之類的工作,只在需要的時候執行一次。
一個 ECS cluster 可以建立多個 ECS service,每個 ECS service 會指定一個 task definition 及希望執行的 task 數量(task desired count)。
task 的數量由ECS service scheduler 負責維持,它以我們指定的 scheduling strategy 來決定 task 要放在哪台 container instance 上(task placement)。如果有 task fail 或 stop,ECS service scheduler 會啟動新的 task 以確保 task 數量。service 的 scheduling strategy 也被稱為 service type,有 REPLICA 跟 DAEMON 兩種。
接下來我們會啟動一個 MySQL container 作為 Database,然後建立一個基本的 ECS Service 並且看到 Laravel 的歡迎畫面。
跟第一天在本機跑 Laravel 的 container 一樣,Laravel 需要連一台 MySQL database。實務上可以用任何 EC2 instance 連得到的 Database,也不一定要是 MySQL,端看需求而定。這裡我們重複利用安裝 Gitlab Runner 的 EC2 instance(不是用來跑 ECS service 的 EC2 instance 喔!!)來執行 MySQL container 作為 Database。
啟動 EC2 instance 後 ssh 進去,跟在本機一樣用 docker run
來啟動 MySQL container:
$ sudo docker run -d \
-e MYSQL_ALLOW_EMPTY_PASSWORD=true \
-e MYSQL_DATABASE=laravel \
-p 3306:3306 \
--name dbserv \
--restart=always \
mysql:8.0
跟本機不同的是這次我們將 MySQL 使用的 port 3306 mapping 到 host 上,讓其他機器連這台 EC2 instance 的 port 3306 時會連到 container 中的 MySQL server。另外也用 --restart=always
參數讓 MySQL container 被關閉後會重新啟動,在 EC2 instance 會開開關關的時候很方便!(不然每次開機 EC2 instance 都要連進來啟動 MySQL container)
一樣要注意上述指令僅是為了實驗,一般不會容許空白密碼,Laravel 也不該用 root 連 database,應該要為這個 application 建立一個 user 並且設定適當權限。
接著就能在 Gitlab CI/CD variable 裡的 ENV
變數設定 EC2 instance 的 public IP 然後 build & push image 了!(小提示:如果沒有 push code 卻想執行 pipeline,可以進到 pipelines 頁面點右上角的 Run Pipeline。)
進入之前新增的 cluster,點選 service 的 create:
Compute configuration 我們先用比較簡單的 Launch type,並且使用 EC2:
來到 deployment 相關設定。我們要 deploy 的是一個會持續執行的 Laravel web service,Application type 選擇 Service。接著我們要幫這個 ECS service 指定 task definition,當然是用先前建立的 task definition 啦~版本用 LATEST(如果你有修改過,LATEST 的 revision 不會是 1)。
ECS service type 是 service scheduler 的 strategy,前面說到有兩種:
REPLICA:會啟動並維持 task 在指定的數量,可額外指定 task placement strategy 跟 constraint 來自訂 task 如何分配到 container instance 上
DAEMON:每台 active 並且符合 task 資源要求的 container instance 都會部屬剛好一個 task
兩種 type 通常依據 application 的特性以及需求決定使用哪一個,這邊我們用 REPLICA。其他設定保持預設,點選 Create,然後我們就會在 Services 的頁面看到:
進到 service 看看:
呃,deployment 失敗。為什麼?到 service 的 Deployments 檢查底下的 Event:
再點進一個 task:
???
為什麼 EC2 instance 關掉了?到 auto scaling group 看發現還真的關掉了,而且 desired capacity 變成 0,why???
檢查 auto scaling group 的 Activities,會看到:
看起來是 cluster 的 auto scaling 被觸發,便將 auto scaling group 的 desired capacity 變回 0。這裡牽涉到 cluster 的 auto scaling 比較複雜,我們先用暴力解——把 auto scaling group 的 minimum capacity 調成 1,這樣 auto scaling 就不能把 desired capacity 設成小於 1 的數值。
再回到 ECS service…….就發現它不見了。
AWS 玩我啊???
咳……冷靜。
是這樣的,現在 AWS 新版 web console 會用 CloudFormation 來建立 ECS service,如果 ECS service 無法 deploy 成功,這個 stack 就不算建立成功,預設會被 rollback。可以在 CloudFormation 看到像這樣的 stack(如果沒有可能是 rollback 完後被刪除了):
再重複一次建立 ECS service 的步驟把 service 開回來。
然後它就不讓我建立相同名稱的 ECS service (眼神逐漸死去) :
這時候到 CloudFormation,確認 ECS service 的 stack 的 status 是 ROLLBACK_COMPLETE
後,就可以把那個 stack 給刪除(delete),再重新建立 ECS service 終於成功了。
一樣進到 service 看看 deploy 狀況:
還是 failed 耶!^_^
一樣點進 task 看看:
錯誤不一樣,太好了!(疑?
用 docker run
把 image 抓下來執行看看(注意:如果 tag 用 latest
而且你的機器上已經有這個 tag 的 image 的話,最好要 docker pull
確定是用到最新版的 image)
在…這…畫…面…卡..了…好…一…陣…子……..
然後終於出現 timeout 的 error:
這表示我們的 container 連不上 MySQL server,為什麼呢?
我們先在本機試試看用 mysql client 能不能連線,排除是 container 造成問題的可能:
$ mysql -h [MYSQL_SERVER_IP] -u root
一樣連不上:
ERROR 2002 (HY000): Can't connect to MySQL server on '13.112.194.32' (115)
為什麼咧?
這是剛開始用 AWS 非常容易踩到的問題:Security Group 沒設定好
記得我們在安裝 Gitlab Runner 時設定過 security group 嗎?依照類似的步驟,建立一個新的 security group,讓它 allow port 3306、source 是 0.0.0.0/0
,並且 attach 到跑 MySQL container 的 EC2 instance 上。
security group 在 EC2 service 裡:
從右上角 Create security group,選擇 VPC 與新增 inbound rule 後 create:
新增好 security group 後,要再到 EC2 instance 頁面在裝 MySQL database 的 instance 上按右鍵 ⇒ Security ⇒ Change security groups:
加入剛剛建立的 security group 後 save:
這樣就完成了這台 EC2 instance 的 allow MySQL 的 security group 設定,可以在 instance 的 security tab 看到它身上有的 security group:
設好 security group,讓我們再一次建立 ECS service……
等啊等啊等……
終於看到這個畫面!!
在 Events 看到 service xxx has reached a steady state.
才表示這個 service deploy 好而且穩定了!
最後我們要用瀏覽器確認 Laravel 有執行起來,連到跑 container 的機器看看有沒有出現 Laravel welcome 的畫面,先找到作為 container instance 的 IP address,從 ECS cluster 的 infrastructure 點進 container instance,可以看到 network 資訊:
我們快樂的連上 http://13.115.245.251
吧!
然後就 timeout 連不上。
好~的~又~忘記 security group 了,趕緊把 inbound rule tcp port 80 以及 source 0.0.0.0/0
加進 container instance 的 security group,再連一次~
成功啦!
現在的架構長這樣:
跟昨天很像,我們建立 ECS service 並依照 task definition 啟動一個 task。ECS 會跟 container instance 裡的 ECS agent 溝通,找一台 container instance 來啟動 container。這個 container 的 image 來自 ECR repository。container instance 是在 ASG(Auto Scaling Group)內的,我們額外有開一台 EC2 instance 來跑 MySQL container 跟 Gitlab Runner。
玩具(O)玩完了要記得收拾,不然會被收比較多費用喔~
ECS service 的 task 數量調成 0 關閉 task 們,沒有 running task 後可以到 Cloudformation 把 ECS service 的 stack 砍掉,我們明天會開新的。
現在主要會被收費的是 EC2 instance,所以要把 Auto Scaling Group 的 desired capacity 調成 0(筆者建議 minimum 跟 maximum 也調成 0),確認 instance 都關掉。還有執行 MySQL server 的 EC2 instance 也要 stop。